#include <iostream>
#include <vector>
#include <algorithm>

// 为了提高IO效率，这是OI/ACM竞赛中的常用技巧
#define FAST_IO std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);

// 奶牛结构体，存储了所有必要信息
struct Cow {
    int id;       // 奶牛的原始输入编号 (0 to N-1)
    long long x, y; // 初始坐标
    char dir;     // 方向 'N' 或 'E'
};

// 交叉点/碰撞事件结构体
struct Intersection {
    long long x, y; // 交叉点的坐标
    int north_cow_id; // 参与该交叉点的北向牛的ID
    int east_cow_id;  // 参与该交叉点的东向牛的ID

    // 重载小于运算符，为sort函数提供排序规则
    // 这是这个算法思想的核心：按空间位置排序
    bool operator<(const Intersection& other) const {
        // 主关键字：按 x 坐标从小到大排序
        if (x != other.x) {
            return x < other.x;
        }
        // 次关键字：如果 x 相同，按 y 坐标从小到大排序
        return y < other.y;
    }
};

int main() {
    FAST_IO;

    int N;
    std::cin >> N;

    // 分别存储北向和东向的奶牛
    std::vector<Cow> north_cows;
    std::vector<Cow> east_cows;
    // 使用一个vector来存储所有奶牛的原始信息，方便通过ID快速查找
    std::vector<Cow> all_cows(N);

    for (int i = 0; i < N; ++i) {
        char dir;
        long long x, y;
        std::cin >> dir >> x >> y;
        all_cows[i] = {i, x, y, dir};
        if (dir == 'N') {
            north_cows.push_back({i, x, y, dir});
        } else {
            east_cows.push_back({i, x, y, dir});
        }
    }

    // --- 步骤 1: 生成所有潜在的交叉点 ---
    std::vector<Intersection> intersections;
    for (const auto& n_cow : north_cows) {
        for (const auto& e_cow : east_cows) {
            // 一个北向牛和一个东向牛要相遇，必须满足几何条件：
            // 北向牛在东向牛的右边 (n_cow.x > e_cow.x)
            // 且北向牛在东向牛的下边 (n_cow.y < e_cow.y)
            if (n_cow.x > e_cow.x && n_cow.y < e_cow.y) {
                intersections.push_back({n_cow.x, e_cow.y, n_cow.id, e_cow.id});
            }
        }
    }

    // --- 步骤 2: 按扫描线顺序对所有交叉点进行排序 ---
    // 这是算法的关键！保证了我们总是处理路径上“最先”遇到的障碍
    std::sort(intersections.begin(), intersections.end());

    // --- 步骤 3: 模拟处理排序后的交叉点 ---
    
    // `blame`数组既存储最终结果，也动态更新
    // `blame[i]` = 牛i直接或间接阻挡的奶牛总数
    std::vector<int> blame(N, 0);
    // `stopped`数组标记一头牛是否已经停止
    std::vector<bool> stopped(N, false);

    for (const auto& intersect : intersections) {
        int n_id = intersect.north_cow_id;
        int e_id = intersect.east_cow_id;
        
        // 如果参与这个交叉点的两头牛中，有任何一头已经停下了，
        // 那么这个交叉点上的碰撞就不会发生，直接跳过。
        if (stopped[n_id] || stopped[e_id]) {
            continue;
        }

        // 计算两头牛到达交叉点所需的距离（也即时间，因为速度为1）
        long long dist_n = intersect.y - all_cows[n_id].y; // 北向牛需要向北走的距离
        long long dist_e = intersect.x - all_cows[e_id].x; // 东向牛需要向东走的距离

        if (dist_e < dist_n) { // 东向牛先到，阻挡北向牛
            stopped[n_id] = true; // 北向牛被阻挡，标记为停止
            
            // 关键的责备计算：
            // 东向牛的责备数 += 1 (被它直接阻挡的北向牛) + 北向牛原本的责备数
            // 这是一种动态规划的思想，利用了正确的处理顺序，
            // 使得 `blame[n_id]` 此时已经包含了n_id能阻挡的所有下游奶牛。
            blame[e_id] += 1 + blame[n_id];

        } else if (dist_n < dist_e) { // 北向牛先到，阻挡东向牛
            stopped[e_id] = true; // 东向牛被阻挡，标记为停止
            
            // 同理，更新北向牛的责备数
            blame[n_id] += 1 + blame[e_id];
        }
        // 如果 `dist_n == dist_e`，两牛同时到达，擦肩而过，互不影响，不做任何处理。
    }

    // --- 步骤 4: 输出结果 ---
    // 按照原始输入顺序 (0 to N-1) 输出每头牛的责备数
    for (int i = 0; i < N; ++i) {
        std::cout << blame[i] << "\n";
    }

    return 0;
}
